package ren.xuedao.service.impl;

import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import ren.xuedao.dao.SeckillDao;
import ren.xuedao.dao.SuccessKilledDao;
import ren.xuedao.dao.cache.RedisDao;
import ren.xuedao.dto.ExposerDTO;
import ren.xuedao.dto.SeckillExecution;
import ren.xuedao.entity.Seckill;
import ren.xuedao.entity.SuccessKilled;
import ren.xuedao.enums.SeckillStatEnum;
import ren.xuedao.exception.RepeatKillException;
import ren.xuedao.exception.SeckillCloseException;
import ren.xuedao.exception.SeckillException;
import ren.xuedao.service.SeckillService;

import java.util.*;

@Service
public class SeckillServiceImpl implements SeckillService{

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private final String salt = "abcdefg099";

    @Autowired
    private SeckillDao seckillDao;

    @Autowired
    private RedisDao redisDao;

    @Autowired
    private SuccessKilledDao successKilledDao;
    @Override
    public List<Seckill> getSeckillList() {
        return seckillDao.queryAll(0,4);
    }

    @Override
    public Seckill getById(Long seckillId) {
        return seckillDao.queryById(seckillId);
    }

    @Override
    public ExposerDTO exportSeckillUrl(Long seckillId) {




        // 优化缓存

        Seckill seckill = redisDao.getSeckill(seckillId);
        if (seckill == null) {
            seckill = seckillDao.queryById(seckillId);
            if (Objects.isNull(seckill)) {
                return new ExposerDTO(false,seckillId);
            } else {
                redisDao.putSeckill(seckill);
            }
        }





        Date startTime = seckill.getStartTime();
        Date endTime = seckill.getEndTime();
        Date now = new Date();

        if (now.getTime() <startTime.getTime() || now.getTime() > endTime.getTime()) {
            return new ExposerDTO(false,seckillId,now.getTime(),startTime.getTime(),endTime.getTime());
        }

        String md5 = getMd5(seckillId);

        return new ExposerDTO(true, md5, seckillId);
    }

    private String getMd5(Long seckillId) {
        String base = seckillId + "/" + salt;
        String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
        return md5;
    }

    @Override
    @Transactional
    public SeckillExecution executeSeckill(Long seckillId, Long userPhone, String md5) throws SeckillException, RepeatKillException, SeckillCloseException {
        if (md5 == null || !Objects.equals(md5,md5.equals(getMd5(seckillId)))) {
            throw new SeckillException("你干啥");
        }
        // 执行秒杀逻辑 减库存，记录购买行为
        Date now = new Date();
        int updateCount = seckillDao.reduceNumber(seckillId, now);

        try {
            if (updateCount <= 0){
                throw new SeckillException("关闭了，十分抱歉");
            } else {
                //记录购买行为
                int insertCount = successKilledDao.insertSuccessKilled(seckillId, userPhone);
                //唯一 id phone
                if (insertCount <= 0) {
                    throw new RepeatKillException("重复秒杀");
                } else {
                    SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone);

                    return new SeckillExecution(seckillId, SeckillStatEnum.SUCCESS,successKilled);
                }
            }
        } catch (SeckillCloseException e1) {
            throw e1;
        } catch (RepeatKillException e2) {
            throw e2;
        }catch (Exception ex) {
            // 所有编译期异常转化为运行期异常
            logger.error(ex.getMessage(),ex);
            throw new SeckillException("inner error"+ ex.getMessage());

        }
    }

    @Override
    public SeckillExecution executeSeckillProcedure(Long seckillId, Long userPhone, String md5) {

        if (md5 ==null || Objects.equals(md5,getMd5(seckillId))) {
            return new SeckillExecution(seckillId,SeckillStatEnum.NI_GAN_SHA);
        }

        Date killTime = new Date();
        Map<String,Object> map = new HashMap<>();
        map.put("seckillId",seckillId);
        map.put("phone",userPhone);
        map.put("killTime",killTime);
        map.put("result",null);
        try {
            seckillDao.seckillByPeocedure(map);
            int result = MapUtils.getInteger(map, "result", -2);

            if (result ==1  ){
                SuccessKilled sk = successKilledDao.queryByIdWithSeckill(seckillId,userPhone);
                return new SeckillExecution(seckillId,SeckillStatEnum.SUCCESS,sk);
            } else {
                return new SeckillExecution(seckillId,SeckillStatEnum.stateOf(result));
            }

        } catch (Exception e) {
            logger.error(e.getMessage(),e);
            return new SeckillExecution(seckillId,SeckillStatEnum.INNER_ERROR);
        }

    }

    @Transactional
    public SeckillExecution execute(Long seckillId, Long userPhone, String md5) throws SeckillException, RepeatKillException, SeckillCloseException {
        if (md5 == null || !Objects.equals(md5,md5.equals(getMd5(seckillId)))) {
            return new SeckillExecution(seckillId,SeckillStatEnum.NI_GAN_SHA);
        }


        // 执行秒杀逻辑 减库存，记录购买行为
        Date now = new Date();

        try {
            //记录购买行为
            int insertCount = successKilledDao.insertSuccessKilled(seckillId, userPhone);
            if (insertCount <= 0) {
                throw new RepeatKillException("重复秒杀");
            } else {
                // 减库存，热点商品的竞争
                int updateCount = seckillDao.reduceNumber(seckillId, now);

                if (updateCount <= 0){
                    throw new SeckillException("关闭了，十分抱歉");
                } else {
                    // 秒杀成功
                    SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone);
                    return new SeckillExecution(seckillId, SeckillStatEnum.SUCCESS,successKilled);
                }
            }

        } catch (SeckillCloseException e1) {
            throw e1;
        } catch (RepeatKillException e2) {
            throw e2;
        }catch (Exception ex) {
            // 所有编译期异常转化为运行期异常
            logger.error(ex.getMessage(),ex);
            throw new SeckillException("inner error"+ ex.getMessage());

        }
    }
}
